/**
 * Copyright (c) 2020-2021 浦东大爷 All rights reserved.
 * 机会永远都是留给有准备的人
 */
package com.job.moudle2.task2;

/**
 * @author: Scott.Yu
 * @date: 2020-11-10
 * @description: 编程实现控制台版并支持两人对战的五子棋游戏。
 */
public class Chessboard {

    private char[][] board; // 棋盘数据
    private boolean isWhite; // 判断是否为白色方进行下棋 false为黑色方，true为白色方， 默认黑方先手
    private boolean isEnd; // 判断游戏是否结束

    /**
     * 自定义无参构造方法初始化棋盘
     */
    public Chessboard() {
        board = new char[17][17]; //棋盘是16*16，加上外边框的标注
        initChessboard();
    }

    /**
     * 自定义show方法打印棋盘
     */
    public void show() {
        // 如果isEnd为True的话，则表示游戏结束，打印结束标语。
        if(isEnd) {
            System.out.println("游戏结束，" + (isWhite? "白色方" : "黑色方") + "获胜!");
        }
        if(board != null) {
            for(int x = 0; x < 17; x++){
                for(int y = 0; y < 17; y++){
                    System.out.print(board[x][y] + "\t");
                }
                System.out.println();
            }
        } else {
            System.out.println("棋盘未初始化成功，游戏结束");
            isEnd = true;
        }
    }

    /**
     * 自定义棋盘初始化方法
     */
    private void initChessboard() {
        if(board != null) {
            for(int x = 0; x < 17; x++){
                for(int y = 0; y < 17; y++){
                    if(x == 0 && y == 0)board[x][y] = ' ';
                    else if(x == 0)board[x][y] = (char)((y <= 10)? y+47 : y+86);
                    else if(y == 0)board[x][y] = (char)((x <= 10)? x+47 : x+86);
                    else board[x][y] = '+';
                    System.out.print(board[x][y] + "\t");
                }
                System.out.println();
            }
        } else {
            System.out.println("棋盘未初始化成功，游戏结束");
            isEnd = true;
        }
    }

    /**
     * 自定义下棋的方法，输入数组的x和y坐标，返回是否下棋成功
     */
    public boolean playChess(int x, int y) {
        // 1. 根据输入的X和Y，判断是否有效（包括边界，是否已经落子），无效则返回落子失败的标志
//        if(x > 16 || x < 0 || y > 16 || y < 0 || board[x][y] != '+') return false;
        if(x > 16 || x < 0 || y > 16 || y < 0) {
            System.out.println("你的棋子都掉在地上了！！！");
            return false;
        } else if(board[x][y] != '+') {
            System.out.println("你走的位置已经有人落子，重新选");
            return false;
        }
        // 2. 如果输入有效，则根据isWhite的标志位，对相应的位置进行赋值同时返回落子成功的标志
        board[x][y] = (char)(isWhite? 79 : 64);
        return true;
    }

    /**
     * 自定义方法判断棋局是否结束
     */
    public void referee(int x, int y) {
        // 1. 判断棋局是否结束，是否有获胜方
        // 1.1 棋子判断是否有相连的五个相同的，有则获胜
        // 1.2 判断是否有相同分为4中情况，横向是否有5个相同的，纵向是否有5个相同的，以及两个斜对角线路是否有相同的

        // 2. 如果有获胜方，则结束棋局，isEnd置为true
        isEnd = refereeRow(x, y) || refereeColumn(x, y) || refereeDiagonalUp(x, y) || refereeDiagonalDown(x, y);
        // 3. 如果没有获胜方，则棋局继续，交换棋手
        if(!isEnd)
            isWhite = !isWhite;
    }

    /**
     * 判断横向是否获胜
     * 判断横向是否获胜需要左边两次遍历，统计相同棋子一样的总数。
     */
    private boolean refereeRow(int x, int y) {
        // 本位棋子也算一个所以从1开始计数
        int count = 1;
        // 先从左边遍历，因为左边边界是有一列标识的，所以棋盘边界0号元素不能算为棋盘
        for(int i = y - 1; i  > 0; i--) {
            if(board[x][i] == ((char)(isWhite? 79 : 64))) {
                count++;
            } else {
                break;
            }
        }
        // 再从右边遍历
        for(int i = y + 1; i < 17; i++) {
            if(board[x][i] == ((char)(isWhite? 79 : 64))) {
                count++;
            } else {
                break;
            }
        }
        // 总数大于等于5的情况，判断获胜 否则返回false
        if(count >= 5) return true; //
        return false;
    }
    /**
     * 判断纵向是否获胜
     * 纵向的逻辑和横向逻辑一样，XY坐标换一下
     */
    private boolean refereeColumn(int x, int y) {
        int count = 1; // 本位棋子也算一个所以从1开始计数
        // 先从左边遍历，因为上边的是有标识边界的，所以0号元素不能算为棋盘
        for(int i = x - 1; i  > 0; i--) {
            if(board[i][y] == ((char)(isWhite? 79 : 64))) {
                count++;
            } else {
                break;
            }
        }
        // 再从右边遍历
        for(int i = x + 1; i < 17; i++) {
            if(board[i][y] == ((char)(isWhite? 79 : 64))) {
                count++;
            } else {
                break;
            }
        }
        // 总数大于等于5的情况，判断获胜 否则返回false
        if(count >= 5) return true; //
        return false;
    }
    /**
     * 判断斜下对角线是否获胜
     */
    private boolean refereeDiagonalDown(int x, int y) {
        int count = 1;
        // 从本位棋子先向斜上方遍历，棋盘的左边和上边是有标识，所以棋盘边界不允许访问0号元素
        for(int i = 1; x-i > 0 && y - i > 0; i ++) {
            if(board[x-i][y-i] == ((char)(isWhite? 79 : 64))) {
                count++;
            } else {
                break;
            }
        }
        for(int i = 1; x+i < 17&& y + i < 17; i ++) {
            if(board[x+i][y+i] == ((char)(isWhite? 79 : 64))) {
                count++;
            } else {
                break;
            }
        }
        // 总数大于等于5的情况，判断获胜 否则返回false
        if(count >= 5) return true; //
        return false;
    }
    /**
     * 判断斜上对角线是否获胜
     */
    private boolean refereeDiagonalUp(int x, int y) {
        int count = 1;
        // 从本位棋子先向斜下方遍历，棋盘的上边是有标识，所以棋盘边界不允许访问0号元素
        for(int i = 1; x+i < 17 && y - i > 0; i ++) {
            if(board[x+i][y-i] == ((char)(isWhite? 79 : 64))) {
                count++;
            } else {
                break;
            }
        }
        for(int i = 1; x-i > 0&& y + i < 17; i ++) {
            if(board[x-i][y+i] == ((char)(isWhite? 79 : 64))) {
                count++;
            } else {
                break;
            }
        }
        // 总数大于等于5的情况，判断获胜 否则返回false
        if(count >= 5) return true; //
        return false;
    }

    public void setWhite(boolean white) {
        isWhite = white;
    }

    public void setEnd(boolean end) {
        isEnd = end;
    }

    public boolean isWhite() {
        return isWhite;
    }

    public boolean isEnd() {
        return isEnd;
    }
}
